package com.entrogy.utils;

import com.baidu.aip.face.AipFace;
import org.json.JSONObject;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.HashMap;

public class BDFaceDetect {
    public static AipFace client;
    public static final String APP_ID = "11703572";
    public static final String API_KEY = "Q5lWwOmr6bZEU0HeWvBfWgiR";
    public static final String SECRET_KEY = "MFzX5PRRBhUQsvBpClzck6nof0x9HNNm";
    public static final int baseSize = 450 * 450;
    public static final int targetLen = 1000 * 1024;

    /**
     * 将内存中的图片信息转换jpg的base64 最后要去掉\r\n!!!!!!
     *
     * @param BufferedImage 输入图片
     * @return String base64编码
     */
    public static String getBase64ByBufferedImage_zw(BufferedImage bImg) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        String base64 = "";
        try {
            ImageIO.write(bImg, "png", out);
            byte[] imgdata = out.toByteArray();
            BASE64Encoder encoder = new BASE64Encoder();
            base64 = encoder.encode(imgdata);
            base64 = base64.replaceAll("\r\n", "");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // System.out.println(base64);
        return base64;
    }

    /**
     * 根据新的长宽缩放切割好的图
     *
     * @param subImg 切割后的图 int， newWidth 新的宽，newHeight 新的长
     * @return BufferedImage 缩放好的图
     */
    public BufferedImage getScaledImg_zw(BufferedImage subImg, int newWidth, int newHeight) {
        BufferedImage outImg = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
        Graphics g = outImg.getGraphics();
        g.drawImage(subImg.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH), 0, 0, null);
        g.dispose();
        return outImg;
    }

    /**
     * 根据坐标和长宽，从原图中截图,越界的话就只截到边界
     *
     * @param subImg 切割后的图 int， newWidth 新的宽，newHeight 新的长
     * @return BufferedImage 缩放好的图
     */
    public BufferedImage getSubImg(int left, int top, int width, int height, BufferedImage srcImg) {
        int oriWidth = srcImg.getWidth();
        int oriHeight = srcImg.getHeight();
        BufferedImage subImg = null;
        if (width > 0 && height > 0) {
            if (left < 0) {
                left = 0;
            }
            if (top < 0) {
                top = 0;
            }
            if ((left + width) >= oriWidth) {
                width = oriWidth - left - 1;
            }
            if ((top + height) >= oriHeight) {
                height = oriHeight - top - 1;
            }
            subImg = srcImg.getSubimage(left, top, width, height);
        }
        return subImg;
    }

    /**
     * 检测的人脸区域有点小，要扩大
     *
     * @param subImg 切割后的图 int， newWidth 新的宽，newHeight 新的长
     * @return BufferedImage 缩放好的图
     */
    public int[] getNewFaceInfo(int[] faceInfo) {
        int[] newFaceInfo = new int[7];
        newFaceInfo[0] = faceInfo[0];
        newFaceInfo[0] = faceInfo[1];
        newFaceInfo[0] = faceInfo[6];
        // 2top 3left 4width 5height
        int top = faceInfo[2];
        int left = faceInfo[3];
        int width = faceInfo[4];
        int height = faceInfo[5];
        float scale = 2f;
        newFaceInfo[2] = top - (int) (((scale - 1) / 2) * height);
        newFaceInfo[3] = left - (int) (((scale - 1) / 2) * width);
        newFaceInfo[4] = (int) (width * scale);
        newFaceInfo[5] = (int) (height * scale);

        return newFaceInfo;
    }

    /**
     * 给定原图和人脸位置，通过切割和缩放得到base64大小符合的，里面可以保存裁剪后的图片和缩放后的图片
     *
     * @param BufferedImage srcImg 原图， int[] faceInfo [0]返回结果 [1]脸的数量 [2]和顶部距离
     *                      [3]和左边距离 [4]宽 [5]高 [6]旋转角度
     * @return 返回BufferedImage格式的最终图片
     */
    public BufferedImage getImgOut_zw(BufferedImage srcImg, int[] faceInfo) {
        // 检测的人脸区域有点小，要扩大
        int[] newFaceInfo = getNewFaceInfo(faceInfo);
        int left = newFaceInfo[3], top = newFaceInfo[2], width = newFaceInfo[4], height = newFaceInfo[5];
        // 裁剪
        BufferedImage subImg = getSubImg(left, top, width, height, srcImg);

        // 输出裁剪后的图片////////////////////////////
//		try {
//			ImageIO.write(subImg, "jpg",new File("subImg.jpg"));
//			subImg.flush();
//		} catch (IOException e1) {
//			// TODO Auto-generated catch block
//			e1.printStackTrace();
//		}
        //////////////////////////////////////////

        double scale;
        int newWidth = width;
        int newHeight = height;
        // System.out.println(newWidth+" "+newHeight);
        if (subImg != null) {
            String base64 = "";
            BufferedImage scaledImg = null;
            int count = 0;
            do {
                scale = (count++ == 0) ? Math.sqrt((double) (baseSize) / (width * height)) : 0.97f;// 450*450的jpg对应的base64的长度是31896，先直接缩放到这个大小附近
                newWidth = (int) (newWidth * scale);
                newHeight = (int) (newHeight * scale);
                scaledImg = getScaledImg_zw(subImg, newWidth, newHeight);
                base64 = getBase64ByBufferedImage_zw(scaledImg);
            } while (base64.length() > targetLen);

            // 输出缩放后的图片////////////////////////////
//			try {
//				ImageIO.write(scaledImg,"jpg",new File("scaledImg.jpg"));
//				scaledImg.flush();
//			} catch (IOException e) {
//				// TODO Auto-generated catch block
//				e.printStackTrace();
//			}
            //////////////////////////////////////////

            return scaledImg;
        } else {
            return null;
        }
    }

    /**
     * 测试用的，看看获得的base64是不是对的
     *
     * @param String base64
     * @return 返回byte[]
     */
    public byte[] getBytes_hxy(String base64) {
        // 对字节数组字符串进行Base64解码并生成图片
        if (base64 == null) // 图像数据为空
            return null;
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            // Base64解码
            byte[] imgBytes = decoder.decodeBuffer(base64);
            for (int i = 0; i < imgBytes.length; ++i) {
                if (imgBytes[i] < 0) {
                    // 调整异常数据
                    imgBytes[i] += 256;
                }
            }

            return imgBytes;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 读取百度人脸检测api返回的信息
     *
     * @param JSONObject retFromBD 百度返回的json消息
     * @return 返回faceInfo [0]返回结果 [1]脸的数量 [2]和顶部距离 [3]和左边距离 [4]宽 [5]高 [6]旋转角度
     */
    public int[] getFaceInfo_hxy(JSONObject retFromBD) {
        // 0:result,是否result为空
        // 1：face_num,人脸的个数
        // 2-6：top,left,width,height,rotation
        int[] info = new int[7];
        Object _resultO = retFromBD.get("result");

        // 筛选result不为空的
        if (_resultO.equals("") || _resultO.equals(null) || _resultO == null) {
            info[0] = 0;
            return info;
        }
        info[0] = 1;

        // 筛选face_num为1的
        JSONObject _result = (JSONObject) _resultO;
        int _face_num = _result.getInt("face_num");
        if (_face_num != 1) {
            info[1] = _face_num;
            return info;
        }
        info[1] = 1;

        // 获得location
        JSONObject _facelocation = _result.getJSONArray("face_list").getJSONObject(0).getJSONObject("location");
        int _top = _facelocation.getInt("top");
        int _left = _facelocation.getInt("left");
        int _width = _facelocation.getInt("width");
        int _height = _facelocation.getInt("height");
        int _rotation = _facelocation.getInt("rotation");
        info[2] = _top;
        info[3] = _left;
        info[4] = _width;
        info[5] = _height;
        info[6] = _rotation;
        return info;
    }

    /**
     * 图片裁剪并调整大小
     *
     * @param int[] faceInfo 裁剪的一些参数, BufferedImage srcImg 原图
     * @return 返回JSONObject
     *         "result":0/1/2代表无人脸、一个人脸、多个人脸,"width":新的宽度,"height":新的高度,"picbase64":base64码
     */
    public JSONObject getFace_hxy(int[] faceInfo, BufferedImage srcImg) {
        JSONObject res = new JSONObject();
        int result = faceInfo[0];
        if (result == 0) {
            res.put("result", 0);
            res.put("width", 0);
            res.put("height", 0);
            res.put("picbase64", "");
            return res;
        } else {
            int faceNum = faceInfo[1];
            if (faceNum != 1) {
                res.put("result", 2);
                res.put("width", 0);
                res.put("height", 0);
                res.put("picbase64", "");
                return res;
            } else {
                // 炜哥的函数,脸框的大小需要在这里面调
                BufferedImage scaledImg = getImgOut_zw(srcImg, faceInfo);
                int newWidth = scaledImg.getWidth();
                int newHeight = scaledImg.getHeight();
                res.put("result", 1);
                res.put("width", newWidth);
                res.put("height", newHeight);
                res.put("picbase64", getBase64ByBufferedImage_zw(scaledImg));
                return res;
            }
        }
    }

    /**
     * 初始化Aip
     *
     * @return AipFace
     */
    public void initAipace() {
        if (client == null) {
            client = new AipFace(APP_ID, API_KEY, SECRET_KEY);
            // 可选：设置网络连接参数
            client.setConnectionTimeoutInMillis(2000);
            client.setSocketTimeoutInMillis(60000);
        }
    }

    /**
     * 总函数入口
     *
     * @return 返回JSONObject
     *         "result":0/1/2代表无人脸、一个人脸、多个人脸,"width":新的宽度,"height":新的高度,"picbase64":base64码
     */
    public JSONObject getAvailableBase64(BufferedImage srcImg) {
        initAipace();

        // 设置detect参数
        String imageBase = getBase64ByBufferedImage_zw(srcImg);
        String imageType = "BASE64";
        HashMap<String, String> options = new HashMap<String, String>();
        options.put("max_face_num", "3");

        // 人脸检测并返回,调用百度api
        JSONObject retFromBD = client.detect(imageBase, imageType, options);
        // System.out.println("**************************百度返回的结果***************************");
        System.out.println(retFromBD.toString());
        // System.out.println("************************************************************");

        // 从百度人脸检测结果中获取人脸的信息，不一定是一个人脸
        int[] faceInfo = getFaceInfo_hxy(retFromBD);
        // System.out.println("faceinfo:result,faceNum,top,left,width,height,rotation");
        // System.out.println(Arrays.toString(faceInfo));

        // 根如果是一张人脸就获得人脸区域并调整大小否则直接返回错误
        JSONObject ret = getFace_hxy(faceInfo, srcImg);

        // 调用接口
        return ret;
    }

    public static void main(String[] args) {
        // 模拟BufferedImage[]，从本地读了一张图片
        InputStream inputStream = null;
        BufferedImage srcImg = null;
        try {
            inputStream = new FileInputStream("hxy.jpg");
            srcImg = ImageIO.read(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

        /*
         *
         * input:图片在内存中的buffer，BufferedImage类型，一定不能为null output：json { result:0,
         * //0：没有检测到人脸；1：一张人脸；2：多余1张人脸 picbase64:"",
         * //大小为小于30KB的***jpg压缩格式***的base64码，只有在result为1时才有正确的值，否则为空字符串, width:0,
         * //新图片的宽 height:0 //新图片的高 } result为0和2时需要返回界面提醒用户重拍
         * 为1时需要把搞好的图片返回界面给用户看看，用户同意了就再用mqtt传这个json，不同意就再拍（因为百度的人脸检测的位置有可能不准）
         */
        // 调用这两行就行
        BDFaceDetect bdFaceDetect = new BDFaceDetect();
        JSONObject ret = bdFaceDetect.getAvailableBase64(srcImg);
        // System.out.println(ret.toString(2));

        // 这里可以看返回base64还原的图片
        if (ret.getInt("result") == 1) {
            byte[] newImgByte = bdFaceDetect.getBytes_hxy(ret.getString("picbase64"));
            // 生成jpeg图片
            String imgFilePath = "new.jpg";// 新生成的图片
            OutputStream out;
            try {
                out = new FileOutputStream(imgFilePath);
                out.write(newImgByte);
                out.flush();
                out.close();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}
